/*	--- 密钥交换算法 --- 


对称加密算法解决了数据加密的问题。我们以AES加密为例，在现实世界中，小明要向路人甲发送一个加密文件，他可以先生成一个AES密钥，对文件进行加密，然后把加密文件发送给对方。因为对方要解密，就必须需要小明生成的密钥。

现在问题来了：如何传递密钥？

在不安全的信道上传递加密文件是没有问题的，因为黑客拿到加密文件没有用。但是，如何如何在不安全的信道上安全地传输密钥？

要解决这个问题，密钥交换算法即DH算法：Diffie-Hellman算法应运而生。


------------------------------------------------


DH算法解决了密钥在双方不直接传递密钥的情况下完成密钥交换，这个神奇的交换原理完全由数学理论支持。

我们来看'DH算法'交换密钥的步骤。假设甲乙双方需要传递密钥，他们之间可以这么做：

	1. 甲首选选择一个素数p(一个正整数,如果只有1和它本身两个因数,则叫做素数,也叫做质数。),例如509; 底数g,任选,例如5; 随机数a,例如123. 然后计算A = g^a mod p,结果是'215'. 然后,甲发送p＝509, g=5, A=215给乙;

	2. 乙方收到后,也选择一个随机数b. 例如,456, 然后计算B = g^b mod p,结果是'181'. 乙再同时计算s = A^b mod p ,结果是121；

	3. 乙把计算的 B = 181 发给甲, 甲计算s＝B^a mod p的余数, 计算结果与乙算出的结果一样, 都是121。


整合统计：
	甲
		1.素数p, 底数g, 随机数a
			A = g^a mod p
		发送p, g, A给乙(没有发'随机数a')

	乙
		1.接收甲发的p, g, A

		2.选择一个随机数b, 计算B = g^b mod p

		3.再计算 s = A^b mod p (以'甲计算的结果'代替 底数g)

		4.将B发给甲

	甲
		1.接收乙发的B

		2.计算 s = B^a mod p(与乙计算的 A^b mod p 公式因素调换)


所以最终双方协商出的密钥s是121。注意到这个密钥s并没有在网络上传输。而通过网络传输的p，g，A和B是无法推算出s的，因为实际算法选择的素数是非常大的。


所以，更确切地说，DH算法是一个密钥协商算法，双方最终协商出一个共同的密钥，而这个密钥不会通过网络传输。

	实际校验(甲、乙)：
		甲：  s = A ^ b mod p = (g ^ a mod p) ^ b mod p

		乙：  s = B ^ a mod p = (g ^ b mod p) ^ a mod p 


------------------------


所以，更确切地说，DH算法是一个密钥协商算法，双方最终协商出一个共同的密钥，而这个密钥不会通过网络传输。

如果我们把

	'a'看成甲的私钥,'A'看成甲的公钥,
	'b'看成乙的私钥，'B'看成乙的公钥,

'DH算法'的本质就是: 双方各自生成自己的私钥'a / b'和公钥'计算的'A / B', 

私钥仅对自己可见, 然后交换公钥，

并根据自己的私钥和对方的公钥，生成最终的密钥secretKey，DH算法通过数学定律保证了双方e各自计算出的secretKey是相同的。

	私钥a x 公钥B = 最终的密钥secretKey
	私钥b x 公钥A = 最终的密钥secretKey

使用Java实现DH算法的代码如下：	    */
import java.math.BigInteger;
import java.security.*;
import java.security.spec.*;

import javax.crypto.KeyAgreement;

public class Main {
	public static void main(String[] args) {
		// Bob 和 Alice
		Person bob = new Person("Bob");
		Person alice = new Person("Alice");

		// 各自生成KeyPair
		bob.generateKeyPair();
		alice.generateKeyPair();

	// 双方交换各自的PublicKey
		// Bob根据Alice的PublicKey生成自己的本地密钥
		bob.generateSecretKey(alice.publicKey.getEncoded());

		// Alice根据Bob的PublicKey生成自己的本地密钥
		alice.generateSecretKey(bob.publicKey.getEncoded());

		// 检查双方的本地密钥是否相同
		bob.printKeys();
		alice.printKeys();

		// 双方得SecretKey相同, 后续通信将使用SecretKey作为'密钥'进行 AES加密/解密
	}
}

class Person {
	public final String name;

	public PublicKey publicKey;
	private PrivateKey privateKey;
	private byte[] secretKey;

	public Person(String name) {
		this.name = name;
	}

	// 生成本地KeyPair
	public void generateKeyPair() {
		try {
			KeyPairGenerator kpGen = KeyPairGenerator.getInstance("DH"); // 密钥对生成器
			kpGen.initialize(512);  

			KeyPair kp = kpGen.generateKeyPair(); // 密钥对

			this.privateKey = kp.getPrivate(); 
				// KeyPair.getPrivate();
			this.publicKey = kp.getPublic(); 
				// KeyPair.getPublic();

		} catch (GeneralSecurityException e) {
			throw new RuntimeException(e);
		}
	}

	// 建立SecretKey
	// receivedPubKeyBytes -> alice.publicKey.getEncoded()
	public void generateSecretKey(byte[] receivedPubKeyBytes) {
		try {
			// 从byte[]恢复到PublicKey
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(receivedPubKeyBytes); 
				// new X509EncodedKeySpec(alice.publicKey.getEncoded())

			KeyFactory kf = KeyFactory.getInstance("DH");
			PublicKey receivedPublicKey = kf.generatePublic(keySpec);
				// KeyFactory.generatePublic()

			// 钥协议
			KeyAgreement keyAgreement = KeyAgreement.getInstance("DH"); // 使用'DH算法'
			keyAgreement.init(this.privateKey); 
				// 初始化自己的'私钥' KeyAgreement.init(私钥)
			keyAgreement.doPhase(receivedPublicKey, true); 
				// 逐步做  KeyAgreement.doPhase(接收公钥, true)

			// 生成SecretKey密钥
			this.secretKey = keyAgreement.generateSecret();
				// init(私钥), doPhase(接收公钥, true),2步后
				// 通过KeyAgreement.generateSecret()可生成基于'接收公钥+自己私钥' = 密钥

		} catch (GeneralSecurityException e) {
			throw new RuntimeException(e);
		}
	}

	public void printKeys() {
		System.out.println("Name: %s\n", this.name);

		System.out.println("Private Key: %x\n", new BigInteger(1, this.privateKey.getEncoded()));
		System.out.println("Public key: %x\n", new BigInteger(1, this.publicKey.getEncoded()));
		System.out.println("Secret key: %x\n", new BigInteger(1, this.secretKey));
	}
}


// 但是DH算法并未解决中间人攻击，即甲乙双方并不能确保与自己通信的是否真的是对方。消除中间人攻击需要其他方法。



/*	---- 密钥交换算法 の 小结 ---- 

1. DH算法是一种密钥交换协议，通信双方通过不安全的信道协商密钥，然后进行对称加密传输。

2. DH算法没有解决中间人攻击。


*/








